package com.xyh.transaction.service.imp;

import com.xyh.transaction.common.Current;
import com.xyh.transaction.common.StatisticTab;
import com.xyh.transaction.common.UserRole;
import com.xyh.transaction.entity.Goods;
import com.xyh.transaction.entity.GoodsStatistic;
import com.xyh.transaction.entity.dto.StatisticDto;
import com.xyh.transaction.entity.vo.CurveData;
import com.xyh.transaction.entity.vo.StatisticVo;
import com.xyh.transaction.entity.vo.TabVo;
import com.xyh.transaction.exception.BusinessException;
import com.xyh.transaction.mapper.GoodsMapper;
import com.xyh.transaction.mapper.GoodsStatisticMapper;
import com.xyh.transaction.mapper.UserMapper;
import com.xyh.transaction.service.StatisticService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StatisticServiceImpl implements StatisticService {


    @Autowired
    private GoodsStatisticMapper goodsStatisticMapper;

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public StatisticVo getStatistic(Integer tab) {
        StatisticVo statisticVo = new StatisticVo();
        Integer userId = Current.getCurrentUser().getUser().getId();

        Goods goods = new Goods();
        // 如果是非管理员则查看自己发布商品的所有数据
        if (Current.getCurrentUser().getRoles().stream()
                .noneMatch(role -> role.getId().equals(UserRole.Admin.getCode()))) {
            goods.setCreateBy(userId);
        }
        List<Integer> goodsIds = goodsMapper.findAll(goods).stream()
                .map(Goods::getId)
                .collect(Collectors.toList());

        StatisticDto statisticDto = new StatisticDto();
        if (goodsIds.isEmpty()) {
            statisticVo.setTotalHits(0L);
            statisticVo.setFemaleHits(0L);
            statisticVo.setMaleHits(0L);
            statisticVo.setTodayHits(0L);
            statisticVo.setYesterdayHits(0L);
            statisticVo.setGuestHits(0L);
            statisticVo.setUserHits(0L);
            return statisticVo;
        }
        statisticDto.setGoodsIds(goodsIds);

        switch (StatisticTab.values()[tab]) {
            case LAST_YEAR:
                // 设置起始时间为一年前的今天
                LocalDateTime startTimeLastYear = LocalDateTime.now().minusYears(1);
                // 设置结束时间为当前时间
                LocalDateTime endTimeLastYear = LocalDateTime.now();
                // 将起始时间和结束时间设置到 statisticDto 中
                statisticDto.setStartTime(startTimeLastYear);
                statisticDto.setEndTime(endTimeLastYear);
                break;
            case LAST_HALF_YEAR:
                // 设置起始时间为半年前的今天
                LocalDateTime startTimeLastHalfYear = LocalDateTime.now().minusMonths(6);
                // 设置结束时间为当前时间
                LocalDateTime endTimeLastHalfYear = LocalDateTime.now();
                // 将起始时间和结束时间设置到 statisticDto 中
                statisticDto.setStartTime(startTimeLastHalfYear);
                statisticDto.setEndTime(endTimeLastHalfYear);
                break;
            case LAST_SEVEN_DAYS:
                // 设置起始时间为七天前的今天
                LocalDateTime startTimeLastSevenDays = LocalDateTime.now().minusDays(7);
                // 设置结束时间为当前时间
                LocalDateTime endTimeLastSevenDays = LocalDateTime.now();
                // 将起始时间和结束时间设置到 statisticDto 中
                statisticDto.setStartTime(startTimeLastSevenDays);
                statisticDto.setEndTime(endTimeLastSevenDays);
                break;
            case LAST_MONTH:
                // 设置起始时间为一个月前的今天
                LocalDateTime startTimeLastMonth = LocalDateTime.now().minusMonths(1);
                // 设置结束时间为当前时间
                LocalDateTime endTimeLastMonth = LocalDateTime.now();
                // 将起始时间和结束时间设置到 statisticDto 中
                statisticDto.setStartTime(startTimeLastMonth);
                statisticDto.setEndTime(endTimeLastMonth);
                break;
            case LAST_THREE_MONTHS:
                // 设置起始时间为三个月前的今天
                LocalDateTime startTimeLastThreeMonths = LocalDateTime.now().minusMonths(3);
                // 设置结束时间为当前时间
                LocalDateTime endTimeLastThreeMonths = LocalDateTime.now();
                // 将起始时间和结束时间设置到 statisticDto 中
                statisticDto.setStartTime(startTimeLastThreeMonths);
                statisticDto.setEndTime(endTimeLastThreeMonths);
                break;
            default:
                log.error("错误的tab类型");
                throw new BusinessException("错误的tab类型！");
        }

        List<GoodsStatistic> goodsStatistics = goodsStatisticMapper.findAll(statisticDto);

        // 游客访问量
        long guestHits = goodsStatistics.stream()
                .filter(goodsStatistic -> goodsStatistic.getUserId() == 0).count();

        // 用户访问量
        long userHits = goodsStatistics.size() - guestHits;

        // 统计不同性别的访问数量(不包含游客)
        Map<Integer, Long> genderMap = goodsStatistics.stream()
                .filter(goodsStatistic -> goodsStatistic.getUserId() != 0)
                .collect(Collectors.groupingBy(
                        goodsStatistic -> userMapper.findById(goodsStatistic.getUserId()).getGender(),
                        Collectors.counting()));

        // 获取昨天和今天的日期时间范围
        LocalDateTime yesterdayStart = LocalDate.now().minusDays(1).atStartOfDay();
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);

        // 分别统计昨天和今天的访问量
        long todayHits = countHitsInTimeRange(goodsStatistics, todayStart, todayEnd);
        long yesterdayHits = countHitsInTimeRange(goodsStatistics, yesterdayStart, todayStart);

        // 统计各年龄段的访问量
        ArrayList<CurveData> curveData = new ArrayList<>();
        // 计算年龄段的范围
        int ageSegmentWidth = 120 / 10; // 年龄段宽度
        List<Integer> ageSegments = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            ageSegments.add(i * ageSegmentWidth); // 年龄段起始范围
        }
        // 统计各年龄段的访问量
        Map<Integer, Long> ageSegmentMap = goodsStatistics.stream()
                .filter(goodsStatistic -> goodsStatistic.getUserId() != 0)
                .map(goodsStatistic -> userMapper.findById(goodsStatistic.getUserId()).getAge())
                .map(age -> {
                    // 将用户的年龄映射到对应的年龄段
                    int segmentIndex = Math.min(age / ageSegmentWidth, 9); // 年龄段索引
                    return ageSegments.get(segmentIndex); // 年龄段起始范围
                })
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        // 填充缺失的年龄段并将访问量设置为 0
        for (Integer ageSegment : ageSegments) {
            ageSegmentMap.putIfAbsent(ageSegment, 0L);
        }
        // 按年龄段的起始范围排序，确保结果有序
        ageSegmentMap.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .forEach(entry -> {
                    Integer ageSegmentStart = entry.getKey();
                    Long count = entry.getValue();
                    CurveData data = new CurveData();
                    // 设置年龄段的起始范围和访问量
                    data.setSpotName(ageSegmentStart + "-" + (ageSegmentStart + ageSegmentWidth - 1));
                    data.setValue(count);
                    curveData.add(data);
                });


        // 构造统计结果对象
        statisticVo.setTotalHits((long) goodsStatistics.size());
        statisticVo.setFemaleHits(genderMap.getOrDefault(0, 0L));
        statisticVo.setMaleHits(genderMap.getOrDefault(1, 0L));
        statisticVo.setTodayHits(todayHits);
        statisticVo.setYesterdayHits(yesterdayHits);
        statisticVo.setGuestHits(guestHits);
        statisticVo.setUserHits(userHits);
        statisticVo.setCurveDatas(curveData);

        return statisticVo;
    }

    private long countHitsInTimeRange(List<GoodsStatistic> goodsStatistics, LocalDateTime start, LocalDateTime end) {
        return goodsStatistics.stream()
                .filter(statistic -> statistic.getAccessTime().isAfter(start) && statistic.getAccessTime().isBefore(end))
                .count();
    }

    @Override
    public List<TabVo> getTabList() {
        return Arrays.stream(StatisticTab.values()).map(statisticTab -> {
            TabVo tabVo = new TabVo();
            tabVo.setLabel(statisticTab.getDetail());
            tabVo.setName(statisticTab.ordinal());
            return tabVo;
        }).collect(Collectors.toList());
    }
}
